﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Bankeren.Engine.Cards;
using System.Threading;

namespace Bankeren.Engine
{
    internal class Round
    {
        #region Fields

        private Game game;
        private int dealer;
        private int roundNumber;
        private Dictionary<int, int> bids;
        private Dictionary<int, int> taken;
        private Trick currentTrick;
        private Card trump;
        private int numCards;

        #endregion

        #region Properties

        internal Game Game
        {
            get
            {
                return this.game;
            }
        }

        internal Card Trump
        {
            get
            {
                return this.trump;
            }
        }

        #endregion

        #region Ctor

        public Round(Game game, int dealer, int roundNumber)
        {
            this.game = game;
            this.dealer = dealer;
            this.roundNumber = roundNumber;
            this.bids = new Dictionary<int, int>();
            this.taken = new Dictionary<int, int>();
            foreach (Player player in game.Players.Values)
            {
                this.taken.Add(player.Seat, 0);
            }
            this.numCards = roundNumber > 10 ? 21 - roundNumber : roundNumber;
        }

        #endregion

        #region Methods

        #region Private Methods

        private Dictionary<int, int> CalculatePoints()
        {
            Dictionary<int, int> points = new Dictionary<int, int>();
            for (int i = 0; i < Game.MAXPLAYERS; i++)
            {
                int playerBids = bids[i];
                int playerTaken = taken[i];
                if (playerTaken == playerBids)
                {
                    if (numCards == 2 && playerBids / numCards == 1)
                        points.Add(i, 24);
                    else if (numCards > 2 && (double)(playerBids / numCards) >= 0.5)
                        points.Add(i, 20 + (2 * playerBids));
                    else
                        points.Add(i, 10 + playerBids);
                }
                else
                    points.Add(i, 0);
            }
            return points;
        }

        private int DetermineLead()
        {
            int lead = (dealer + 1) % Game.MAXPLAYERS;
            for (int i = 0, seat = (lead + 1) % Game.MAXPLAYERS; i < Game.MAXPLAYERS; i++)
            {
                if (bids[seat] > bids[lead])
                    lead = seat;
                seat = ++seat % Game.MAXPLAYERS;
            }
            return lead;
        }

        #endregion

        #region Internal Methods

        internal Dictionary<int, int> PlayRound()
        {
            Log(string.Format("Starting. Dealer: {0}", game.Players[dealer]));
            Deck deck = new Deck();
            deck.FillAndShuffle();
            Log("Dealing cards...");
            // TODO: Start with player next to dealer
            foreach (Player player in game.Players.Values)
            {
                List<Card> cards = new List<Card>();
                for (int i = 1; i <= numCards; i++)
                    cards.Add(deck.Draw());
                player.DealCards(cards);
            }
            Log("Cards dealt");
            trump = deck.Draw();
            Log("Trump is " + trump);
            Log("Waiting for bids...");
            foreach (Player player in game.Players.Values)
                player.RoundStarted(roundNumber, numCards, dealer, trump);
            while (bids.Count < Game.MAXPLAYERS)
                Thread.Sleep(10);
            Log("Bids received");
            foreach (Player player in game.Players.Values)
                player.Bids(bids);
            int lead = DetermineLead();
            int trickNumber = 1;
            while (trickNumber <= numCards)
            {
                // Start new trick
                currentTrick = new Trick(this, lead, trickNumber);
                // Set lead for next trick, and increment bids taken.
                lead = currentTrick.PlayTrick();
                taken[lead]++;
                trickNumber++;
            }
            Dictionary<int, int> points = CalculatePoints();
            foreach (Player player in game.Players.Values)
                player.RoundEnded(points);
            foreach (int seat in points.Keys)
                Log(game.Players[seat] + ": " + points[seat]);
            Log("Finished");
            return points;
        }

        internal void PlaceBid(int seat, int bid)
        {
            this.bids.Add(seat, bid);
            Log(string.Format("Bid from {0}: {1}", game.Players[seat], bid));
        }
        
        internal bool PlayCard(Player player, Card card)
        {
            return currentTrick.PlayCard(player, card);
        }

        internal void Log(string format, params object[] args)
        {
            game.Log("Round " + roundNumber + ": " + format, args);
        }

        #endregion

        #endregion
    }
}
